package com.sunda.spmswms.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sunda.spmscommon.Constans;
import com.sunda.spmscommon.ResponseResult;
import com.sunda.spmscommon.feign.IFeignServiceCommon;
import com.sunda.spmswms.entity.*;
import com.sunda.spmswms.mapper.WhsOutMapper;
import com.sunda.spmswms.service.*;
import feign.FeignException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.*;

import static com.sunda.spmscommon.Constans.*;

/**
 * <p>
 * 出库任务表 服务实现类
 * </p>
 *
 * @author Wayne
 * @since 2021-04-23
 */
@Service
public class WhsOutServiceImpl extends ServiceImpl<WhsOutMapper, WhsOut> implements IWhsOutService {

    @Autowired
    IShelfOnTaskService iShelfOnTaskService;

    @Autowired
    ISapDeliveryNoteLogService iSapDeliveryNoteLogService;

    @Autowired
    IWhsStorageInventoryService iWhsStorageInventoryService;

    @Autowired
    IWhsInventoryInfoService iWhsInventoryInfoService;

    @Autowired
    IWhsOperateLogService iWhsOperateLogService;

    @Autowired
    ISapPackageBoxService iSapPackageBoxService;

    @Autowired
    ISapPackageListDtlService iSapPackageListDtlService;

    @Autowired
    IFeignServiceCommon iFeignServiceCommon;
    @Autowired
    IWhsStorageService iWhsStorageService;


    @Override
    public WhsOut getWhsOutByUuid(String uuid) {
        return this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid));
    }

    @Override
    public WhsOut getWhsOut(String werks, String whsLocationCode, String packageList, String goodsType, String dateOfLoading, String cabinetSerialNumber) {
        QueryWrapper<WhsOut> queryWrapper = new QueryWrapper<WhsOut>()
                .eq("WERKS", werks)
                .eq("WHS_LOCATION_CODE", whsLocationCode)
                .eq("PACKAGE_LIST", packageList)
                .eq("GOODS_TYPE", goodsType)
                .eq("DATE_OF_LOADING", dateOfLoading)
                .eq("CABINET_SERIAL_NUMBER", cabinetSerialNumber);
        return this.baseMapper.selectOne(queryWrapper);
    }

    @Override
    public int insertWhsOut(WhsOut whsOut) {
        return this.baseMapper.insert(whsOut);
    }

    /**
     * {
     * "werks": ["CN01"],
     * "whsLocationCode": ["1011", "1061"],
     * "whsOutNo": "",
     * "packageList": "",
     * "goodsType": "",
     * "dateOfLoading": "",
     * "cabinetSerialNumber": "",
     * "taskStatus": "0,1",
     * "pageNo": 1,
     * "pageSize": 20
     * }
     *
     * @param doc
     * @return
     */
    @Override
    public IPage<WhsOut> getWhsOutList(JSONObject doc) {
        if (!doc.containsKey("werks") || doc.getJSONArray("werks").size() == 0 || !doc.containsKey("whsLocationCode") || doc.getJSONArray("whsLocationCode").size() == 0) {
            return null;
        }
        if (doc.getJSONArray("whsLocationCode").size() == 1 && doc.getJSONArray("whsLocationCode").get(0) == null) {
            return null;
        }
        int pageNo = StringUtils.isEmpty(doc.getString("pageNo")) ? 1 : (doc.getInteger("pageNo") < 1 ? 1 : doc.getInteger("pageNo"));
        int pageSize = StringUtils.isEmpty(doc.getString("pageSize")) ? 20 : doc.getInteger("pageSize");
        Page<WhsOut> page = new Page<>(pageNo, pageSize);
        List<String> werks = doc.getJSONArray("werks").toJavaList(String.class);
        List<String> whsLocationCode = doc.getJSONArray("whsLocationCode").toJavaList(String.class);
        QueryWrapper<WhsOut> queryWrapper = new QueryWrapper<WhsOut>();
        if (werks.size() > 0) {
            queryWrapper.in("WERKS", werks);
        }
        if (whsLocationCode.size() > 0) {
            queryWrapper.in("WHS_LOCATION_CODE", whsLocationCode);
        }
        if (doc.containsKey("whsOutNo") && StringUtils.isNotEmpty(doc.getString("whsOutNo"))) {
            queryWrapper.eq("WHS_OUT_NO", doc.getString("whsOutNo"));
        }
        if (doc.containsKey("packageList") && StringUtils.isNotEmpty(doc.getString("packageList"))) {
            queryWrapper.eq("PACKAGE_LIST", doc.getString("packageList"));
        }
        if (doc.containsKey("goodsType") && StringUtils.isNotEmpty(doc.getString("goodsType"))) {
            queryWrapper.eq("GOODS_TYPE", doc.getString("goodsType"));
        }
        if (doc.containsKey("dateOfLoading") && StringUtils.isNotEmpty(doc.getString("dateOfLoading"))) {
            queryWrapper.eq("DATE_OF_LOADING", doc.getString("dateOfLoading").replaceAll("-", ""));
        }
        if (doc.containsKey("cabinetSerialNumber") && StringUtils.isNotEmpty(doc.getString("cabinetSerialNumber"))) {
            queryWrapper.eq("CABINET_SERIAL_NUMBER", doc.getString("cabinetSerialNumber"));
        }
        if (doc.containsKey("taskStatus") && StringUtils.isNotEmpty(doc.getString("taskStatus"))) {
            List<String> taskStatus = Arrays.asList(doc.getString("taskStatus").split(","));
            queryWrapper.in("TASK_STATUS", taskStatus);
            //queryWrapper.eq("TASK_STATUS", doc.getString("taskStatus"));
        }
        return this.baseMapper.selectPage(page, queryWrapper);
    }

    /**
     * {
     * "werks": ["CN01"],
     * "whsLocationCode": ["1011", "1061"],
     * "whsOutNo": "",
     * "packageList": "",
     * "goodsType": "",
     * "dateOfLoading": "",
     * "cabinetSerialNumber": "",
     * "taskStatus": "0,1",
     * "pageNo": 1,
     * "pageSize": 20
     * }
     *
     * @param doc
     * @return
     */
    @Override
    public Page<Map<String, Object>> getWhsOutListPage(JSONObject doc) {
        if (!doc.containsKey("werks") || doc.getJSONArray("werks").size() == 0 || !doc.containsKey("whsLocationCode") || doc.getJSONArray("whsLocationCode").size() == 0) {
            return null;
        }
        if (doc.getJSONArray("whsLocationCode").size() == 1 && doc.getJSONArray("whsLocationCode").get(0) == null) {
            return null;
        }
        int pageNo = StringUtils.isEmpty(doc.getString("pageNo")) ? 1 : (doc.getInteger("pageNo") < 1 ? 1 : doc.getInteger("pageNo"));
        int pageSize = StringUtils.isEmpty(doc.getString("pageSize")) ? 20 : doc.getInteger("pageSize");
        List<String> werks = doc.getJSONArray("werks").toJavaList(String.class);
        List<String> whsLocationCode = doc.getJSONArray("whsLocationCode").toJavaList(String.class);
        String whsOutNo = StringUtils.isEmpty(doc.getString("whsOutNo")) ? null : doc.getString("whsOutNo");
        String packageList = StringUtils.isEmpty(doc.getString("packageList")) ? null : doc.getString("packageList");
        String goodsType = StringUtils.isEmpty(doc.getString("goodsType")) ? null : doc.getString("goodsType");
        String dateOfLoading = StringUtils.isEmpty(doc.getString("dateOfLoading")) ? null : doc.getString("dateOfLoading");
        String cabinetSerialNumber = StringUtils.isEmpty(doc.getString("cabinetSerialNumber")) ? null : doc.getString("cabinetSerialNumber");
        List<String> taskStatus = StringUtils.isEmpty(doc.getString("taskStatus")) ? null : Arrays.asList(doc.getString("taskStatus").split(","));
        Page<Map<String, Object>> pages = new Page<>(pageNo, pageSize);
        List<Map<String, Object>> pageMaData = this.baseMapper.getWhsOutListPage(pages, werks, whsLocationCode, whsOutNo, packageList, goodsType, dateOfLoading, cabinetSerialNumber, taskStatus);
        pages.setRecords(pageMaData);
        return pages;
    }


    @Override
    public List<Map<String, Object>> getWhsOutDtl(String goodsType, String uuid) {
        /** 获取 备件货 出库任务明细行项目 */
        if ("X".equals(goodsType)) {
            /** 有额外增加出库箱码的情况，额外出库箱码没有行号，存入出库任务UUID。此处查询要考虑这种情况。 */
            return this.baseMapper.getPackageListDtlX(uuid);
        }
        /** 获取 贸易货 出库任务明细行项目 */
        if ("M".equals(goodsType)) {
            return this.baseMapper.getPackageListDtlM(uuid);
        }
        return null;
    }

    /**
     * 根据出库任务表头获取出库任务明细,下架任务明细信息
     */
    @Override
    public ResponseResult getWhsOutDtlX(String uuid) {
        /** 获取 备件货 出库任务明细行项目 */

        /** 有额外增加出库箱码的情况，额外出库箱码没有行号，存入出库任务UUID。此处查询要考虑这种情况。 */
        ResponseResult shelfOnDtls = iShelfOnTaskService.getShelfOffWithDtlsByRelatedUUID(uuid);
        if (shelfOnDtls.getCode() != 200) {
            return shelfOnDtls;
        }
        return shelfOnDtls.add("whsOutDtl", this.baseMapper.getPackageListDtlX(uuid)).add("whsOut", this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid)));


    }

    @Override
    public List<Map<String, Object>> getBoxNoteDtl(String boxNote) {
        if (StringUtils.isEmpty(boxNote)) {
            return null;
        }
        return this.baseMapper.getBoxNoteDtl(boxNote);
    }

    @Override
    public int initShelfOnTask(String userId, String createDate, String uuid) {
        WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid));
        if (whsOut == null || !"0".equals(whsOut.getTaskStatus())) {
            /** 出库任务不存在或该出库任务已下发下架任务 */
            return -1;
        }
        int resp = iShelfOnTaskService.genShelfOnTask(userId, createDate, whsOut);
        /** 下架任务初始化完成，将出库任务状态修改为 执行中1 */
        if (resp > 0) {
            whsOut.setTaskStatus("1");
            int result = this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", uuid));
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(uuid, userId, WHS_OUT_TASK_UPDATE);
            return result;
        }
        return -1;
    }

    /**
     * {
     * "pdaSn": "1234567890",
     * "shelfOnTask": {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "currentStatus": "7",
     * "targetStatus": "3",
     * "relateDocNumber": "23444500c18c4f898950ac5de825bb01"
     * },
     * "shelfOnTaskDtls": [{
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 10,
     * "boxNote": "TEST-1009-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513",
     * "unplanned": ""
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 20,
     * "boxNote": "TEST-1009-0102",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513",
     * "unplanned": ""
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 30,
     * "boxNote": "TEST-1003-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b512",
     * "unplanned": ""
     * }],
     * "whsOut": {
     * "uuid": "23444500c18c4f898950ac5de825bb01",
     * "cabinetNumber": "JZX20217788",
     * "currentStatus": 1,
     * "targetStatus": 2,
     * "werks": "CN01",
     * "whsLocationCode": "1011",
     * "submitDate": "2021-05-12",
     * "actualLoadingDate": "2021-05-12",
     * "monitorLoading": "zhang san",
     * "loadingType": "A",
     * "remarks": "remarks",
     * "cabinetCheckResult": "ok",
     * "leadSeal":"123456789"
     * },
     * "whsOutDtl": [{
     * "packageList": "PL1001",
     * "boxNote": "TEST-1009-0101",
     * "actualOperateQty": 1,
     * "unplanned": ""
     * }, {
     * "packageList": "PL1001",
     * "boxNote": "TEST-1009-0102",
     * "actualOperateQty": "",
     * "unplanned": ""
     * }],
     * "remainingBoxNote": [{
     * "boxNote": "TEST-1003-0101",
     * "quantity": 1
     * }]
     * }
     *
     * @param doc
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult updateWhsOut(JSONObject doc, String userId) {
        ResponseResult responseResult = checkDataValidate(doc);
        if (responseResult.getCode() == 100) {
            return responseResult;
        }
        try {
            /** 先删除下架任务明细，新建下架任务详情，增加下架任务更新日志，更新下架任务表头为已完成，记录下架任务完成日志 */
            int uShelfOnTaskDtl = updateShelfOnTaskDtl(doc, userId);
            if (uShelfOnTaskDtl < 0) {
                throw new Exception("下架任务更新过程执行异常 updateShelfOnTaskDtl ");
            }
            /** 更新储位库存表，扣减下架明细中的箱码库存，记录储位库存台账 */
            int uStorageInventory = updateStorageInventory(doc, userId);
            if (uStorageInventory < 0) {
                throw new Exception("储位库存更新过程执行异常 updateStorageInventory ");
            }
            /** i.更新出库任务明细，添加出库任务更新日志；
             * ii.应出未出的箱，写回实出 空 或 0 即可；
             * iii.出了计划之外的箱，新增数据到装箱单箱码关系表中；
             * iv.更新出库任务表头状态为已完成，更新出库任务行项目状态为已完成，记录出库任务更新和完成日志。
             * */
            int uWhsOutTask = updateWhsOutTask(doc, userId);
            if (uWhsOutTask < 0) {
                throw new Exception("出库任务更新过程执行异常 updateWhsOutTask ");
            }

            /** 更新仓库库存表，扣减出库明细中的箱码仓库库存，记录仓库库存台账 */
            int uWhsInventoryInfo = updateWhsInventoryInfo(doc, userId);
            if (uWhsInventoryInfo < 0) {
                throw new Exception("仓库库存更新过程执行异常 updateWhsInventoryInfo ");
            }

            /** 如果下架明细和出库明细存在差异项则转化成上架任务，新建上架任务，新建上架任务明细，记录上架任务新建日志 */
            int aShelfOnTask = addShelfOnTask(doc, userId);
            if (aShelfOnTask < 0) {
                throw new Exception("下架任务生成过程异常 addShelfOnTask ");
            }

            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("请求执行过程出错请重试:" + e.toString());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult withDrawWhsOutX(String whsOutUuid, String currentStatus, String userId) {
        try {
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            if (whsOut == null) {
                return ResponseResult.error("出库任务 " + whsOutUuid + " 不存在");
            }
            if (!currentStatus.equals(whsOut.getTaskStatus())) {
                return ResponseResult.error("出库任务 " + whsOut.getWhsOutNo() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
            }

            String werks = whsOut.getWerks();
            String whsLocationCode = whsOut.getWhsLocationCode();

            // 获取当前出库单所在的工厂仓库的储位，并获取第一个做为撤回后的储位
            List<String> sWerks = new ArrayList<String>();
            sWerks.add(werks);
            List<String> sWhsLocationCode = new ArrayList<String>();
            sWhsLocationCode.add(whsLocationCode);
            List<WhsStorage> whsStorages = iWhsStorageService.getAllWhsStorageList(sWhsLocationCode, sWerks);
            if (null == whsStorages || whsStorages.size() < 0) {
                throw new Exception("不能获取到仓库的储位信息 werks" + werks + " whsLocationCode:" + whsLocationCode);
            }
            WhsStorage targetStorage = whsStorages.get(0);

            //获取出任务行明细
            List<Map<String, Object>> whsOutDtl = getWhsOutDtl("X", whsOutUuid);
            if (null == whsOutDtl || whsOutDtl.size() <= 0) {
                return ResponseResult.error("无法获取出库任务明细");
            }

            // 判断是否有未完成的下架任务，发果有未完成的下架任务，将下架任务标为完成
            List<ShelfOnTask> shelfOffTasks = iShelfOnTaskService.getUnClosedTasks(whsOutUuid);
            if (null != shelfOffTasks && shelfOffTasks.size() > 0) {
                return ResponseResult.error("有关联的未完成的上、下架任务");
            }

            ResponseResult withdrawSAP = new ResponseResult();
            // 如果已经提交到了SAP，需要首先回撤SAP
            if (currentStatus.equals(WHS_OUT_Status_Completed) || currentStatus.equals(WHS_OUT_Status_SuccessedSAP)) {
                // TO DO: 出库任务撤销SAP
                //ResponseResult withdrawSAP = ResponseResult.success();
                withdrawSAP = whsOutCancel(whsOut.getPackageList(), whsOutUuid, userId);
                if (withdrawSAP.getCode() != 200) {
                    return withdrawSAP;
                }
            }

            // 判断是否有未完成的下架任务，发果有未完成的下架任务，将下架任务标为完成
//            if (null != shelfOffTasks && shelfOffTasks.size() > 0) {
//                for (int i = 0; i < shelfOffTasks.size(); i++) {
//                    if (!shelfOffTasks.get(i).getTaskStatus().equals("3")) {
//                        iShelfOnTaskService.updateShelfOnTaskStatus(shelfOffTasks.get(i).getUuid(), "3");
//                        iSapDeliveryNoteLogService.insertSapDeliveryNoteLogN(shelfOffTasks.get(i).getUuid(), userId, Constans.WHS_OUT_WITHDRAW_EndShelfOff, "");
//                    }
//                }
//            }

            /** i.将出库任务明细中的每一行加回储位库存，忽略实际数量为0的行，添加出库任务更新日志；
             * iv.更新出库任务表头状态为0-待处理，，记录出库任务更新日志,再次提交时会重新生成新的下架任务
             * */
            /**
             * 2021-09-24
             * kpzhang@cn.ibm.com: 加箱码库存时,只处理实际出库数量大于0的行(actualOperateQty>0)
             */
            for (int i = 0; i < whsOutDtl.size(); i++) {
                Map<String, Object> dtlObj = whsOutDtl.get(i);
                double actualOperateQty = null != dtlObj.get("actualOperateQty") ? Double.parseDouble(dtlObj.get("actualOperateQty").toString()) : 0;
                String boxNote = null != dtlObj.get("boxNote") ? dtlObj.get("boxNote").toString() : null;
                if (actualOperateQty > 0) {
                    // 验证箱码在储位库存中不存在，如果存在，则抛出异常
                    WhsStorageInventory isExistStorage = iWhsStorageInventoryService.getBoxNoteStorage(boxNote);
                    if (null != isExistStorage) {
                        throw new Exception("箱码: " + boxNote + " 已经存在于储位: " + isExistStorage.getUuid());
                    }
                    // 加箱码储位库存
                    ResponseResult insertRes = iWhsStorageInventoryService.insertStorageInventoryX(targetStorage.getUuid(), boxNote, "", userId);
                    if (insertRes.getCode() != 200) {
                        throw new Exception("增加储位库存失败 boxNote: " + boxNote);
                    }
                    // 加日志
                    iWhsOperateLogService.insertWhsOperateLog(
                            Constans.WhsOperateLog_DocumentType_NanShaStorageLog,
                            targetStorage.getUuid(),
                            "",
                            boxNote,
                            1.0,
                            Constans.WhsOperateLog_OperationType_ShelfOn,
                            whsOutUuid,
                            Constans.WHS_OUT_WITHDRAW,
                            userId,
                            Constans.WHS_OUT_WITHDRAW + " WhsOUTNO:" + whsOut.getWhsOutNo() + "储位:" + targetStorage.getStorageNo(),
                            werks,
                            whsLocationCode);
                    /** 更新仓库库存表，增加出库明细中的箱码仓库库存，记录仓库库存台账 */
                    if (iWhsInventoryInfoService.addWhsInventory(werks, whsLocationCode, boxNote, 1.0, userId) < 0) {
                        throw new Exception("增加仓库库存失败 boxNote:" + boxNote);
                    }
                    iWhsOperateLogService.insertWhsOperateLog(
                            Constans.WhsOperateLog_DocumentType_NanShaWHSLog,
                            "",
                            "",
                            boxNote,
                            1.0,
                            Constans.WhsOperateLog_OperationType_InHouse,
                            whsOutUuid,
                            Constans.WHS_OUT_WITHDRAW,
                            userId,
                            Constans.WHS_OUT_WITHDRAW + " WhsOUTNO:" + whsOut.getWhsOutNo() + "储位:" + targetStorage.getStorageNo(),
                            werks,
                            whsLocationCode);
                }   
            }
            // 更改原单据状态，增加单据操作日志
            whsOut.setTaskStatus("0");
            this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogN(whsOutUuid, userId, Constans.WHS_OUT_WITHDRAW, "");
            updateSapPackageList(userId, whsOut.getPackageList(), "1");
            return ResponseResult.success()
                    .add("whsOut", whsOut)
                    .add("targetStorage","所有箱码存于储位:" + targetStorage.getStorageNo() +" (All boxex be keept in storage: " + targetStorage.getStorageNo() +")")
                    .add("sendSapReturnedMessage", withdrawSAP.getData().get("sendSapReturnedMessage"))
                    .add("dataToSap", withdrawSAP.getData().get("dataToSap"));
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("请求执行过程出错请重试:" + e.toString());
        }
    }

    /**
     * 初步检查数据的合法性，检查出库任务状态是否能被更新，检查下架任务状态是否能被更新，检查下架任务中储位库存是否够，检查出库任务中仓库库存是否够
     */
    public ResponseResult checkDataValidate(JSONObject doc) {
        try {
            if (!doc.containsKey("whsOut") || !doc.containsKey("whsOutDtl") ||
                    doc.getJSONArray("whsOutDtl").size() == 0 || !doc.containsKey("shelfOnTask") ||
                    !doc.containsKey("shelfOnTaskDtls") || doc.getJSONArray("shelfOnTaskDtls").size() == 0) {
                return ResponseResult.error("请求参数错误请重试");
            }
            String werks = doc.getJSONObject("whsOut").getString("werks");
            String whsLocationCode = doc.getJSONObject("whsOut").getString("whsLocationCode");

            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            String whsOutUuid = whsOutObj.getString("uuid");
            String currentStatus = whsOutObj.getString("currentStatus");
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            if (whsOut == null) {
                return ResponseResult.error("出库任务 " + whsOutUuid + " 不存在");
            }
            if (!currentStatus.equals(whsOut.getTaskStatus())) {
                return ResponseResult.error("出库任务 " + whsOut.getWhsOutNo() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
            }

            JSONObject shelfOnTaskObj = doc.getJSONObject("shelfOnTask");
            String shelfOnTaskUuid = shelfOnTaskObj.getString("uuid");
            String shelfOnTaskCurrentStatus = shelfOnTaskObj.getString("currentStatus");
            ShelfOnTask shelfOnTask = iShelfOnTaskService.getShelfOnTaskByTaskUuid(shelfOnTaskUuid);
            if (shelfOnTask == null) {
                return ResponseResult.error("下架任务 " + shelfOnTaskUuid + " 不存在");
            }
            if (!shelfOnTaskCurrentStatus.equals(shelfOnTask.getTaskStatus())) {
                return ResponseResult.error("下架任务 " + shelfOnTask.getTaskId() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
            }

            JSONArray shelfOnTaskDtls = doc.getJSONArray("shelfOnTaskDtls");
            for (int i = 0; i < shelfOnTaskDtls.size(); i++) {
                JSONObject dtl = shelfOnTaskDtls.getJSONObject(i);
                String boxNote = dtl.getString("boxNote");
                String targetStorage = dtl.getString("targetStorage");
                double actualOperateQty = StringUtils.isEmpty(dtl.getString("actualOperateQty")) ? 0 : dtl.getDouble("actualOperateQty");
                /** 实际操作数量 > 0 才需要校验对应储位库存是否足够 */
                if (actualOperateQty > 0) {
                    if (targetStorage == null) {
                        return ResponseResult.error("请求参数错误");
                    }
                    String availableQty = iWhsStorageInventoryService.getAvailableQtyByUuidBoxNote(targetStorage, boxNote);
                    if (availableQty == null || Double.parseDouble(availableQty) < actualOperateQty) {
                        return ResponseResult.error("箱码：" + boxNote + "  在储位：" + targetStorage + " 可用库存不足");
                    }
                }
            }

            JSONArray whsOutDtl = doc.getJSONArray("whsOutDtl");
            for (int i = 0; i < whsOutDtl.size(); i++) {
                JSONObject dtl = whsOutDtl.getJSONObject(i);
                String boxNote = dtl.getString("boxNote");
                double actualOperateQty = StringUtils.isEmpty(dtl.getString("actualOperateQty")) ? 0 : dtl.getDouble("actualOperateQty");
                if (actualOperateQty > 0) {
                    String quantity = iWhsInventoryInfoService.getQuantityByBoxNote(werks, whsLocationCode, boxNote);
                    if (quantity == null || Double.parseDouble(quantity) < actualOperateQty) {
                        return ResponseResult.error("箱码：" + boxNote + "  在仓库：" + werks + whsLocationCode + " 可用库存不足");
                    }
                }
            }
            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error(e.toString());
        }

    }

    /**
     * {"shelfOnTask": {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "currentStatus": "7",
     * "targetStatus": "3",
     * "relateDocNumber": "23444500c18c4f898950ac5de825bb01"
     * },
     * "shelfOnTaskDtls": [{
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 10,
     * "boxNote": "TEST-1009-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513"
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 20,
     * "boxNote": "TEST-1009-0102",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513"
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 30,
     * "boxNote": "TEST-1003-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b512"
     * }]}
     * 先删除下架任务明细，新建下架任务详情，增加下架任务更新日志，更新下架任务表头为已完成，记录下架任务完成日志
     */
    int updateShelfOnTaskDtl(JSONObject doc, String userId) {
        try {
            JSONObject shelfOnTaskHead = doc.getJSONObject("shelfOnTask");
            JSONArray shelfOnTaskDtls = doc.getJSONArray("shelfOnTaskDtls");
            String shelfOnTaskUuid = shelfOnTaskHead.getString("uuid");
            String targetStatus = shelfOnTaskHead.getString("targetStatus");
            /** 删除下架任务详情 */
            if (shelfOnTaskUuid != null) {
                iShelfOnTaskService.deleteShelfOnTaskDtl(shelfOnTaskUuid);
            }
            for (int i = 0; i < shelfOnTaskDtls.size(); i++) {
                JSONObject shelfOnTaskDtlObj = shelfOnTaskDtls.getJSONObject(i);
                ShelfOnTaskDtl shelfOnTaskDtl = new ShelfOnTaskDtl();
                shelfOnTaskDtl.setUuid(shelfOnTaskDtlObj.getString("uuid"));
                shelfOnTaskDtl.setTaskRowId(shelfOnTaskDtlObj.getString("taskRowId"));
                shelfOnTaskDtl.setBoxNote(shelfOnTaskDtlObj.getString("boxNote"));
                shelfOnTaskDtl.setQuantity(1.0);
                if (StringUtils.isNotEmpty(shelfOnTaskDtlObj.getString("actualOperateQty"))) {
                    shelfOnTaskDtl.setActualOperateQty(shelfOnTaskDtlObj.getDouble("actualOperateQty"));
                } else {
                    shelfOnTaskDtl.setActualOperateQty(0.0);
                }
                shelfOnTaskDtl.setTargetStorage(shelfOnTaskDtlObj.getString("targetStorage"));
                shelfOnTaskDtl.setUnplanned(shelfOnTaskDtlObj.getString("unplanned"));
                /** 新建下架任务详情，实际操作数量 > 0 的详情才需要被记录下来 */
                if (shelfOnTaskDtl.getActualOperateQty() > 0) {
                    iShelfOnTaskService.insertShelfOnTaskDtl(shelfOnTaskDtl);
                }
            }
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(shelfOnTaskUuid, userId, UPDATE_DEDUCT_SHELF_TASK);
            /** 更新下架任务表头状态为已完成 */
            iShelfOnTaskService.updateShelfOnTaskStatus(shelfOnTaskUuid, targetStatus);
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(shelfOnTaskUuid, userId, CLOSE_DEDUCT_SHELF_TASK);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * {"shelfOnTask": {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "currentStatus": "7",
     * "targetStatus": "3",
     * "relateDocNumber": "23444500c18c4f898950ac5de825bb01"
     * },
     * "shelfOnTaskDtls": [{
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 10,
     * "boxNote": "TEST-1009-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513"
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 20,
     * "boxNote": "TEST-1009-0102",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513"
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 30,
     * "boxNote": "TEST-1003-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b512"
     * }]}
     * 更新储位库存表，删除扣减下架明细中实际操作数量大于 0 的箱码的库存，记录储位库存台账
     *
     * @param doc
     * @param userId
     * @return
     */
    int updateStorageInventory(JSONObject doc, String userId) {
        String whsOutUuid = doc.getJSONObject("shelfOnTask").getString("relateDocNumber");
        JSONObject whsOut = doc.getJSONObject("whsOut");
        try {
            JSONArray shelfOnTaskDtls = doc.getJSONArray("shelfOnTaskDtls");
            for (int i = 0; i < shelfOnTaskDtls.size(); i++) {
                JSONObject shelfOnTaskDtl = shelfOnTaskDtls.getJSONObject(i);
                double actualOperateQty = StringUtils.isEmpty(shelfOnTaskDtl.getString("actualOperateQty")) ?
                        0 : shelfOnTaskDtl.getDouble("actualOperateQty");
                if (actualOperateQty > 0) {
                    String boxNote = shelfOnTaskDtl.getString("boxNote");
                    String targetStorage = shelfOnTaskDtl.getString("targetStorage");
                    /** 删除该箱码的储位库存由于箱码具有唯一性，扣减箱码库存按箱码删除，记录储位操作日志 */
                    iWhsStorageInventoryService.deleteStorageBoxNote(boxNote);
                    iWhsOperateLogService.insertWhsOperateLogBX(targetStorage, boxNote, actualOperateQty, whsOutUuid, SPARE_PARTS_OUT_STORAGE, userId,
                            whsOut.getString("werks"), whsOut.getString("whsLocationCode"));
                }
            }
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * i.更新出库任务明细，添加出库任务更新日志；
     * ii.应出未出的箱，写回实出 空 或 0 即可；
     * iii.出了计划之外的箱，根据箱码遍历出物料，增加到装箱单明细行里，同时新增数据到装箱单箱码关系表中；
     * iv.更新出库任务表头状态为已完成，更新出库任务行项目状态为已完成，记录出库任务更新和完成日志。
     */
    int updateWhsOutTask(JSONObject doc, String userId) {
        String whsOutUuid = doc.getJSONObject("whsOut").getString("uuid");
        try {
            JSONArray whsOutDtls = doc.getJSONArray("whsOutDtl");
            for (int i = 0; i < whsOutDtls.size(); i++) {
                JSONObject whsOutDtlObj = whsOutDtls.getJSONObject(i);
                String packageList = whsOutDtlObj.getString("packageList");
                String boxNote = whsOutDtlObj.getString("boxNote");
                String unplanned = whsOutDtlObj.getString("unplanned");
                double actualOperateQty = StringUtils.isEmpty(whsOutDtlObj.getString("actualOperateQty"))
                        ? 0 : whsOutDtlObj.getDouble("actualOperateQty");
                String containerBrand = whsOutDtlObj.getString("containerBrand");
                String packingSpec = whsOutDtlObj.getString("packingSpec");
                String loadingType = whsOutDtlObj.getString("loadingType");
                /** unplanned = 0 或空 表示原出库任务行；unplanned = 1 表示前端新增的出库任务行；
                 * 计划内的实际操作数量大于 0 才会更新出库明细；计划外的实际操作数量大于 0 才会新增到出库明细中即装箱单明细中，同时加入到装箱单箱码关系表中；
                 * */
//              20210706 注释代码，多次保存实际出库数量为零的行也要更新
//                if (actualOperateQty > 0) {
//                    if ("1".equals(unplanned)) {
//                        /** 多出的箱码，新增数据到装箱单箱码关系表中，unplanned = 1 表示是计划之外新增出库的箱码。不更新出库明细。 */
//                        iSapPackageBoxService.addSapPackageBox(packageList, boxNote, actualOperateQty, "1", whsOutUuid, containerBrand, packingSpec, loadingType);
//                    } else {
//                        iSapPackageBoxService.updateSapPackageBox(packageList, boxNote, actualOperateQty, whsOutUuid, containerBrand, packingSpec, loadingType);
//                    }
//                }

                if ("1".equals(unplanned)){
                    if (actualOperateQty > 0){
                        /** 多出的箱码，新增数据到装箱单箱码关系表中，unplanned = 1 表示是计划之外新增出库的箱码。不更新出库明细。 */
                        iSapPackageBoxService.addSapPackageBox(packageList, boxNote, actualOperateQty, "1", whsOutUuid, containerBrand, packingSpec, loadingType);
                    }
                }else {
                    iSapPackageBoxService.updateSapPackageBox(packageList, boxNote, actualOperateQty, whsOutUuid, containerBrand, packingSpec, loadingType);
                }
            }
            /** 更新出库任务明细表对应的行状态为 2 */
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_TASK_UPDATE);

            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            WhsOut whsOut = new WhsOut();
            whsOut.setCabinetNumber(whsOutObj.getString("cabinetNumber"));
            whsOut.setTaskStatus(whsOutObj.getString("targetStatus"));
            whsOut.setActualLoadingDate(whsOutObj.getString("actualLoadingDate"));
            whsOut.setMonitorLoading(whsOutObj.getString("monitorLoading"));
            whsOut.setLoadingType(whsOutObj.getString("loadingType"));
            whsOut.setRemarks(whsOutObj.getString("remarks"));
            whsOut.setCabinetCheckResult(whsOutObj.getString("cabinetCheckResult"));
            whsOut.setLeadSeal(whsOutObj.getString("leadSeal"));
            if(StringUtils.isNotEmpty(whsOutObj.getString("operateUser"))){
                whsOut.setOperateUser(whsOutObj.getString("operateUser"));
            }

            iSapPackageListDtlService.uSapPackageListDtl(whsOutObj.getString("targetStatus"), whsOutObj.getString("uuid"));
            this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutObj.getString("uuid")));
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_TASK_CLOSE);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 更新仓库库存表，扣减出库明细中的箱码仓库库存，记录仓库库存台账
     */
    int updateWhsInventoryInfo(JSONObject doc, String userId) {
        String whsOutUuid = doc.getJSONObject("whsOut").getString("uuid");
        try {
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            JSONArray whsOutDtlObj = doc.getJSONArray("whsOutDtl");
            String werks = whsOutObj.getString("werks");
            String whsLocationCode = whsOutObj.getString("whsLocationCode");
            for (int i = 0; i < whsOutDtlObj.size(); i++) {
                JSONObject whsOutDtl = whsOutDtlObj.getJSONObject(i);
                String boxNote = whsOutDtl.getString("boxNote");
                double actualOperateQty = StringUtils.isEmpty(whsOutDtl.getString("actualOperateQty"))
                        ? 0 : whsOutDtl.getDouble("actualOperateQty");
                if (actualOperateQty > 0) {
                    iWhsInventoryInfoService.deleteBoxNoteWhsInventory(werks, whsLocationCode, boxNote);
                    iWhsOperateLogService.insertWhsOperateLogBC(boxNote, actualOperateQty, whsOutUuid, SPARE_PARTS_OUT_WHS, userId, werks, whsLocationCode);
                }
            }
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 如果下架明细和出库明细存在差异项则转化成上架任务，新建上架任务，新建上架任务明细，记录上架任务新建日志
     */
    int addShelfOnTask(JSONObject doc, String userId) {
        try {
            String shelfOnTaskUuid = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            JSONArray remainingBoxNote = doc.getJSONArray("remainingBoxNote");
            if (remainingBoxNote.size() > 0) {
                /** 生成（状态1）待上架（类型1）上架任务，生成上架任务明细，记录上架任务初始化日志 */
                ShelfOnTask shelfOnTask = new ShelfOnTask();
                shelfOnTask.setUuid(shelfOnTaskUuid);
                shelfOnTask.setWerks(whsOutObj.getString("werks"));
                shelfOnTask.setWhsLocationCode(whsOutObj.getString("whsLocationCode"));
                shelfOnTask.setCreateUserId(userId);
                shelfOnTask.setCreateDate(doc.getString("submitDate").replaceAll("-", ""));
                shelfOnTask.setTaskStatus("1");
                shelfOnTask.setPdaSN(doc.getString("pdaSn"));
                shelfOnTask.setTaskType("1");
                shelfOnTask.setRelateDocNumber(whsOutObj.getString("uuid"));
                int resp = iShelfOnTaskService.addShelfOnTask(shelfOnTask);

                for (int i = 0; i < remainingBoxNote.size(); i++) {
                    double quantity = StringUtils.isEmpty(remainingBoxNote.getJSONObject(i).getString("quantity")) ?
                            0 : remainingBoxNote.getJSONObject(i).getDouble("quantity");
                    if (quantity > 0) {
                        ShelfOnTaskDtl shelfOnTaskDtl = new ShelfOnTaskDtl();
                        shelfOnTaskDtl.setUuid(shelfOnTaskUuid);
                        shelfOnTaskDtl.setTaskRowId(String.valueOf((i + 1) * 10));
                        shelfOnTaskDtl.setBoxNote(remainingBoxNote.getJSONObject(i).getString("boxNote"));
                        shelfOnTaskDtl.setQuantity(Double.parseDouble(remainingBoxNote.getJSONObject(i).getString("quantity")));
                        iShelfOnTaskService.addShelfOnTaskDtl(shelfOnTaskDtl);
                    }
                }
                /** 记录上架任务关联日志——上架任务初始化；记录出库任务关联日志——上架任务初始化； */
                iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(shelfOnTaskUuid, userId, GENERATE_SHELF_ON_TASK);
                iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutObj.getString("uuid"), userId, GENERATE_SHELF_ON_TASK);
            }
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * {
     * "whsOut": {
     * "uuid": "17e02ac85c8d491b8889e14d0f453057",
     * "werks": "CN01",
     * "whsLocationCode": "1011",
     * "cabinetNumber": "JZX20217788",
     * "currentStatus": 1,
     * "targetStatus": 2,
     * "actualLoadingDate": "2021-05-12",
     * "monitorLoading": "zhang san",
     * "loadingType": "A",
     * "remarks": "remarks",
     * "cabinetCheckResult": "ok",
     * "leadSeal":"123456789"
     * },
     * "whsOutDtl": [{
     * "packageList": "PL1001",
     * "materialNo": "210026516",
     * "actualOperateQty": 10,
     * "storageUuidList": [{
     * "storageUuid": "b95946ac1dab42b79ade3438acb0b511",
     * "qty": 5
     * }, {
     * "storageUuid": "b95946ac1dab42b79ade3438acb0b512",
     * "qty": 5
     * }],
     * "unplanned": ""
     * }, {
     * "packageList": "PL1001",
     * "boxNote": "210026515",
     * "actualOperateQty": "10",
     * "storageUuidList": [{
     * "storageUuid": "b95946ac1dab42b79ade3438acb0b511",
     * "qty": 10
     * }],
     * "unplanned": "1"
     * }]
     * }
     * 贸易货出库任务完成，补单形式.
     * 1.先判断 whsOut.targetStatus 值，1表示保存功能，不做库存检查；2表示提交功能，需检查各物料库存；
     * 2.对于出库明细中 unplanned = '1' 的项则在装箱单明细中新增行；
     * 3.用户提交结果，则更新出库任务表头；记录操作日志；
     * 4.更新出库任务明细/装箱单明细，更新实际出库数量(基本单位)字段；有新增行则新增关联数据；记录操作日志；
     * 5.扣减出库贸易货的储位库存，记录出未出库台账；
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult updateWhsOutM(JSONObject doc, String userId) {
        ResponseResult responseResult = checkDataValidateM(doc);
        if (responseResult.getCode() == 100) {
            return responseResult;
        }
        try {
            int sWhsOut = saveWhsOut(doc, userId);
            if (sWhsOut < 0) {
                throw new Exception("更新出库任务和出库任务明细过程出错 saveWhsOut");
            }

            int uWhsStorage = updateWhsStorage(doc, userId);
            if (uWhsStorage < 0) {
                throw new Exception("更新贸易货储位库存过程出错 updateWhsStorage");
            }
            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("请求执行过程出错请重试:" + e.toString());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult withdrawWhsOutM(String whsOutUuid, String currentStatus, String userId) {
        WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        if (whsOut == null) {
            return ResponseResult.error("出库任务 " + whsOutUuid + " 不存在");
        }
        if (!currentStatus.equals(whsOut.getTaskStatus())) {
            return ResponseResult.error("出库任务 " + whsOut.getWhsOutNo() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
        }
        List<SapPackageListDtl> whsOutDtl = iSapPackageListDtlService.getSapPackageListDtlsByWhsOutUuid(whsOutUuid);
        if (null == whsOutDtl || whsOutDtl.size() <= 0) {
            return ResponseResult.error("获取出库任务明细失败");
        }
        ResponseResult withdrawSAP = new ResponseResult();
        // 如果已经提交到了SAP，需要首先回撤SAP
        if (currentStatus.equals(WHS_OUT_Status_Completed) || currentStatus.equals(WHS_OUT_Status_SuccessedSAP)) {
            // TO DO: 出库任务撤销SAP
            //ResponseResult withdrawSAP = ResponseResult.success();
            withdrawSAP = whsOutCancel(whsOut.getPackageList(), whsOutUuid, userId);
            if (withdrawSAP.getCode() != 200) {
                return withdrawSAP;
            }
        }

        try {
            // 如果已经计算过SPMS的库存，需要原储位加回库存
            if (currentStatus.equals(WHS_OUT_Status_Completed) ||
                    currentStatus.equals(WHS_OUT_Status_SuccessedSAP) ||
                    currentStatus.equals(WHS_OUT_Status_FailedSAP) ||
                    currentStatus.equals(WHS_OUT_Status_PendingSyncSAP)
            ) {
                // 原储位加回库存
                ResponseResult withdrawStorageRes = withdrawWhsOutStorageM(whsOut, whsOutDtl, userId);
                if (withdrawStorageRes.getCode() != 200) {
                    throw new Exception(withdrawStorageRes.getMsg());
                }
            }

            // SAP回撤成功，储位回撤成功
            whsOut.setTaskStatus(WHS_OUT_Status_InProcessing);
            this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_WhsOut_Withdraw);
            updateSapPackageList(userId, whsOut.getPackageList(), "1");
            return ResponseResult.success().add("whsOut", whsOut)
                    .add("sendSapReturnedMessage", withdrawSAP.getData().get("sendSapReturnedMessage"))
                    .add("dataToSap", withdrawSAP.getData().get("dataToSap"));
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("请求执行过程出错请重试:" + e.toString());
        }
    }


    public ResponseResult checkDataValidateM(JSONObject doc) {
        try {
            if (!doc.containsKey("whsOut") || !doc.containsKey("whsOutDtl")) {
                return ResponseResult.error("请求参数错误请重试");
            }
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            String whsOutUuid = whsOutObj.getString("uuid");
            String currentStatus = whsOutObj.getString("currentStatus");
            String targetStatus = whsOutObj.getString("targetStatus");
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            if (whsOut == null) {
                return ResponseResult.error("出库任务 " + whsOutUuid + " 不存在");
            }
            if (!currentStatus.equals(whsOut.getTaskStatus())) {
                return ResponseResult.error("出库任务 " + whsOut.getWhsOutNo() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
            }
            /** 贸易货提交出库结果，需要检查库存信息 */
            if ("2".equals(targetStatus)) {
                JSONArray whsOutDtlObj = doc.getJSONArray("whsOutDtl");
                if (whsOutDtlObj.size() == 0) {
                    return ResponseResult.error();
                }
                for (int i = 0; i < whsOutDtlObj.size(); i++) {
                    JSONObject whsOutDtl = whsOutDtlObj.getJSONObject(i);
                    String materialNo = whsOutDtl.getString("materialNo");
                    double actualOperateQty = StringUtils.isEmpty(whsOutDtl.getString("actualOperateQty")) ? 0 : whsOutDtl.getDoubleValue("actualOperateQty");
                    JSONArray storageUuidList = whsOutDtl.getJSONArray("storageUuidList");
                    if (actualOperateQty > 0) {
                        if (storageUuidList.size() == 0) {
                            return ResponseResult.error("参数结构错误请重试");
                        }
                        for (int j = 0; j < storageUuidList.size(); j++) {
                            String storageUuid = storageUuidList.getJSONObject(j).getString("storageUuid");
                            String qtyString = storageUuidList.getJSONObject(j).getString("qty");
                            double qty = StringUtils.isEmpty(qtyString) ? 0 : Double.parseDouble(qtyString);
                            WhsStorageInventory whsStorageInventory = iWhsStorageInventoryService.getStorageMaterialNo(storageUuid, materialNo);
                            if (whsStorageInventory == null || whsStorageInventory.getQuantity() < qty) {
                                return ResponseResult.error("物料 " + materialNo + " 在储位 " + storageUuid + " 库存不足");
                            }
                        }
                    }
                }
            }
            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    int saveWhsOut(JSONObject doc, String userId) {
        try {
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            JSONArray whsOutDtlArray = doc.getJSONArray("whsOutDtl");
            String whsOutUuid = whsOutObj.getString("uuid");
            String targetStatus = whsOutObj.getString("targetStatus");
            /** 每次保存或提交之前，根据出库任务uuid和计划外的行标记，先删除已有的 计划外的出库行项目，重新建立计划外的行项目数据。 */
            iSapPackageListDtlService.deleteUnplannedSapPackageListDtl(whsOutUuid, "1");
            for (int i = 0; i < whsOutDtlArray.size(); i++) {
                JSONObject whsOutDtl = whsOutDtlArray.getJSONObject(i);
                String packageList = whsOutDtl.getString("packageList");
                String item = whsOutDtl.getString("item");
                String unplanned = whsOutDtl.getString("unplanned");
                String containerBrand = whsOutDtl.getString("containerBrand");
                String colorNumber = whsOutDtl.getString("colorNumber");
                String thickness = whsOutDtl.getString("thickness");
                String actualAvgGrossWeight = whsOutDtl.getString("actualAvgGrossWeight");
                double actualPackingNumber = StringUtils.isEmpty(whsOutDtl.getString("actualPackingNumber")) ?
                        0 : whsOutDtl.getDouble("actualPackingNumber");
                double customsDeclarationNumber = StringUtils.isEmpty(whsOutDtl.getString("customsDeclarationNumber")) ?
                        0 : whsOutDtl.getDouble("customsDeclarationNumber");
                String packingSpec = whsOutDtl.getString("packingSpec");
                String loadingType = whsOutDtl.getString("loadingType");
                double actualOperateQty = StringUtils.isEmpty(whsOutDtl.getString("actualOperateQty")) ?
                        0 : whsOutDtl.getDouble("actualOperateQty");
                String storageUuidList = whsOutDtl.getJSONArray("storageUuidList").toJSONString();

                SapPackageListDtl sapPackageListDtl = new SapPackageListDtl();
                sapPackageListDtl.setSpmsStatus(targetStatus);
                sapPackageListDtl.setUnplanned(unplanned);
                sapPackageListDtl.setActualOperateQty(actualOperateQty);
                sapPackageListDtl.setTempStorage(storageUuidList);
                sapPackageListDtl.setContainerBrand(containerBrand);
                sapPackageListDtl.setColorNumber(colorNumber);
                sapPackageListDtl.setThickness(thickness);
                sapPackageListDtl.setActualAvgGrossWeight(actualAvgGrossWeight);
                sapPackageListDtl.setActualPackingNumber(actualPackingNumber);
                sapPackageListDtl.setCustomsDeclarationNumber(customsDeclarationNumber);
                sapPackageListDtl.setPackingSpec(packingSpec);
                sapPackageListDtl.setLoadingType(loadingType);

                /** 计划外的行需新增数据到装箱单明细中；计划内的贸易货出库，按 装箱单号 + 行号 更新出库明细实际出库数量、出库储位料号数量等信息。已有的行，item不会为空。 */
                if ("1".equals(unplanned)) {
                    sapPackageListDtl.setWhsOutUuid(whsOutUuid);
                    sapPackageListDtl.setPackageList(whsOutDtl.getString("packageList"));
                    sapPackageListDtl.setMaterialNo(whsOutDtl.getString("materialNo"));
                    sapPackageListDtl.setGoodsType("M");
                    iSapPackageListDtlService.insertSapPackageListDtl(sapPackageListDtl);
                } else {
                    iSapPackageListDtlService.updateSapPackageListDtlM(sapPackageListDtl, packageList, item);
                }
            }
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_TASK_UPDATE);

            WhsOut whsOut = new WhsOut();
            whsOut.setCabinetNumber(whsOutObj.getString("cabinetNumber"));
            whsOut.setTaskStatus(targetStatus);
            whsOut.setActualLoadingDate(whsOutObj.getString("actualLoadingDate"));
            whsOut.setMonitorLoading(whsOutObj.getString("monitorLoading"));
            whsOut.setLoadingType(whsOutObj.getString("loadingType"));
            whsOut.setRemarks(whsOutObj.getString("remarks"));
            whsOut.setCabinetCheckResult(whsOutObj.getString("cabinetCheckResult"));
            whsOut.setLeadSeal(whsOutObj.getString("leadSeal"));
            if(StringUtils.isNotEmpty(whsOutObj.getString("operateUser"))){
                whsOut.setOperateUser(whsOutObj.getString("operateUser"));
            }
            this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_TASK_CLOSE);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 撤销贸易货出库，原储位加回库存
     */
    @Transactional(rollbackFor = Exception.class)
    ResponseResult withdrawWhsOutStorageM(WhsOut whsOut, List<SapPackageListDtl> packageListDtls, String userId) {
        try {
            String whsOutUuid = whsOut.getUuid();
            String werks = whsOut.getWerks();
            String whsLocationCode = whsOut.getWhsLocationCode();

            for (int i = 0; i < packageListDtls.size(); i++) {
                SapPackageListDtl sapPackageListDtl = packageListDtls.get(i);
                String materialNo = sapPackageListDtl.getMaterialNo();
                double actualOperateQty = null == sapPackageListDtl.getActualOperateQty() ? 0 : sapPackageListDtl.getActualOperateQty();
                if (actualOperateQty > 0) {
                    JSONArray storageUuidList = JSONArray.parseArray(sapPackageListDtl.getTempStorage());
                    for (int j = 0; j < storageUuidList.size(); j++) {
                        JSONObject storage = storageUuidList.getJSONObject(j);
                        String storageUuid = storage.getString("storageUuid");
                        String qtyString = storage.getString("qty");
                        double qty = StringUtils.isEmpty(qtyString) ? 0 : Double.parseDouble(qtyString);
                        String storageNo = storage.getString("storageNo");
                        if (StringUtils.isEmpty(storageUuid) || StringUtils.isEmpty(storageNo) || qty <= 0) {
                            throw new Exception("error for " + materialNo + " on storageUuid: " +
                                    storageUuid + ",storageNo:" + storageNo +
                                    ", qty: " + qty);
                        }
                        if (qty > 0) {
                            /** 贸易货储位库存加，加成功则记录储位台账 */
                            if (iWhsStorageInventoryService.updateWhsStorageInventoryMOld(storageUuid, materialNo, qty) < 0) {
                                throw new Exception("Update Storage Inventory failed for " + materialNo + " on storageUuid: " +
                                        storageUuid + ",storageNo:" + storageNo +
                                        ", qty: " + qty);
                            }
                            iWhsOperateLogService.insertWhsOperateLog(
                                    Constans.WhsOperateLog_DocumentType_NanShaStorageLog,
                                    storageUuid,
                                    materialNo,
                                    "",
                                    qty,
                                    Constans.WhsOperateLog_OperationType_ShelfOn,
                                    whsOutUuid,
                                    Constans.WHS_WhsOut_Withdraw_ShelfOn,
                                    userId,
                                    Constans.WHS_WhsOut_Withdraw_ShelfOn + materialNo + ":" + qty,
                                    werks,
                                    whsLocationCode);
                        }
                    }
                }
            }
            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error(e.getMessage());
        }
    }

    /**
     * 贸易货出库，扣减储位库存
     */
    int updateWhsStorage(JSONObject doc, String userId) {
        try {
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            JSONArray whsOutDtlArray = doc.getJSONArray("whsOutDtl");
            String whsOutUuid = whsOutObj.getString("uuid");
            String targetStatus = whsOutObj.getString("targetStatus");
            String werks = whsOutObj.getString("werks");
            String whsLocationCode = whsOutObj.getString("whsLocationCode");
            /** targetStatus 为2即提交单据时，才需要计算扣减储位库存 */
            if ("2".equals(targetStatus)) {
                for (int i = 0; i < whsOutDtlArray.size(); i++) {
                    String materialNo = whsOutDtlArray.getJSONObject(i).getString("materialNo");
                    double actualOperateQty = StringUtils.isEmpty(whsOutDtlArray.getJSONObject(i).getString("actualOperateQty"))
                            ? 0 : whsOutDtlArray.getJSONObject(i).getDoubleValue("actualOperateQty");
                    JSONArray storageUuidList = whsOutDtlArray.getJSONObject(i).getJSONArray("storageUuidList");
                    if (actualOperateQty > 0) {
                        for (int j = 0; j < storageUuidList.size(); j++) {
                            String storageUuid = storageUuidList.getJSONObject(j).getString("storageUuid");
                            String qtyString = storageUuidList.getJSONObject(j).getString("qty");
                            double qty = StringUtils.isEmpty(qtyString) ? 0 : Double.parseDouble(qtyString);

                            if (qty > 0) {
                                /** 贸易货储位库存扣减，扣减成功则记录储位台账 */
                                int resp = iWhsStorageInventoryService.updateWhsStorage(storageUuid, materialNo, qty);
                                if (resp > 0) {
                                    iWhsOperateLogService.insertWhsOperateLogMX(storageUuid, materialNo, qty, whsOutUuid, GOODS_OUT_STORAGE, userId, werks, whsLocationCode);
                                }
                            }
                        }
                    }
                }
            }
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }


    /**
     * 首先根据出库任务uuid查询，同一个装箱单下所有  未成功提交SAP的出库任务  数量，不等于 1 则不是最后一次提交SAP
     * 获取出库任务执行结果提交SAP，提交成功则更新出库任务表头、行项目，建立日志。
     * 返回信息包含 出库任务结果表头、出库任务结果行项目、备件货还包括箱码
     */
    @Override
    @Transactional
    public synchronized ResponseResult getWhsOutToSap(String uuid, String userId) {
        try {
            String confirmLastSubmission = "0";
            /** 获取出库任务同一个装箱单下所有  未成功提交SAP  的出库任务数量，来判断当前任务是否是最后一个提交。最后一个提交会多出 PT_STATE 一些字段； */
            /** 20210713 修改最后一次提交判断逻辑：同一个装箱单下的出库任务，状态不为 3（任务关闭），不为4（提交SAP成功）的出库任务数量如果为 1，则表示最后一次提交 */
            String getWhsOutStatusCount = this.baseMapper.getWhsOutStatusCount(uuid);
            if ("1".equals(getWhsOutStatusCount)) {
                confirmLastSubmission = "1";
            }
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid));
            if (whsOut == null || !"2".equals(whsOut.getTaskStatus())) {
                return ResponseResult.error("任务当前状态禁止提交");
            }
            ResponseResult getDataToSap = getDataToSap(uuid, confirmLastSubmission, whsOut.getGoodsType(), whsOut.getPackageList());
            if (getDataToSap.getCode() != 200) {
                return getDataToSap;
            }
//            if (getDataToSap.getCode() == 200){
//                JSONObject dataToSap = JSONObject.parseObject(getDataToSap.getData().get("obj").toString());
//                System.out.println("======" + dataToSap);
//                return getDataToSap;
//            }
            JSONObject dataToSap = JSONObject.parseObject(getDataToSap.getData().get("obj").toString());
            System.out.println(dataToSap);
            JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon((dataToSap)));
            System.out.println(message);
            if (message.containsKey("RESPONSE") && message.getJSONObject("RESPONSE").containsKey("RETURN_DATA")
                    && "S".equals(message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA").getString("EX_TYPE"))) {
                // 海运出库提交SAP暂时没有整单的返回 凭证 和 凭证年份 字段
                this.baseMapper.updateWhsOut(uuid, "4");
                iSapPackageListDtlService.uSapPackageListDtl("4", uuid);
                if ("1".equals(confirmLastSubmission)) {
                    /** 如果是最后一次提交SAP，且成功的话，就将装箱单表头状态信息更新为 2处理完成；行项目在每次成功提交会自动更新出库任务对应行，此处忽略； */
                    updateSapPackageList(userId, whsOut.getPackageList(), "2");
                }
                iSapDeliveryNoteLogService.insertSapRequestRecord(uuid, userId, WHS_OUT_TO_SAP, message.toJSONString(), dataToSap.toJSONString());
                return ResponseResult.success()
                        .add("sapReturnedMessage", message)
                        .add("whsOut", this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid)))
                        .add("whsOutDtl", getWhsOutDtl(whsOut.getGoodsType(), uuid))
                        .add("dataToSap", dataToSap);
            }
            iSapDeliveryNoteLogService.insertSapRequestRecord(uuid, userId, WHS_OUT_TO_SAP_ERROR, message.toJSONString(), dataToSap.toJSONString());
            return ResponseResult.error()
                    .add("sendSapReturnedMessage", message)
                    .add("dataToSap", dataToSap);
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error("出库任务提交SAP出错").add("error", e.getMessage());
        }

    }

    ResponseResult getDataToSap(String whsOutUuid, String confirmLastSubmission, String goodsType, String packageList) {
        try {
            JSONObject obj = new JSONObject();
            JSONObject REQUEST = new JSONObject();
            JSONObject ESB_ATTRS = new JSONObject();
            JSONObject REQUEST_DATA = new JSONObject();
            JSONObject Head = new JSONObject();
            JSONObject MapItems = new JSONObject();
            JSONArray PT_INPUT_LIST = new JSONArray();
            JSONArray PT_STATE = new JSONArray();
            List<Map<String, Object>> PT_CONFIRM = new ArrayList<>();

            ESB_ATTRS.put("App_ID", "SPMS");
            ESB_ATTRS.put("Application_ID", "00020000000002");
            ESB_ATTRS.put("Transaction_ID", UUID.randomUUID().toString());

            Head.put("imImportFlag", "A");

            REQUEST_DATA.put("Head", Head);
            REQUEST_DATA.put("Operation", "Z_ZXD_IMPROT_AND_CONFIRM");
            REQUEST_DATA.put("Type", "IMPROT_AND_CONFIRM");

            List<Map<String, Object>> getItemDtl = new ArrayList<>();
            if ("M".equals(goodsType)) {
                getItemDtl = this.baseMapper.getItemDtlM(whsOutUuid);
            } else {
                getItemDtl = this.baseMapper.getItemDtlX(whsOutUuid);
            }
            if (getItemDtl.size() == 0) {
                return ResponseResult.error("出库结果提交SAP时，获取出库详情过程出错");
            }
            for (int i = 0; i < getItemDtl.size(); i++) {
                JSONObject tempDoc = new JSONObject();
                tempDoc.put("zxdidEx", getItemDtl.get(i).get("itemNo").toString());
                tempDoc.put("zspmsTaskid", whsOutUuid);
                tempDoc.put("xdnum", getItemDtl.get(i).get("packageList").toString());
                tempDoc.put("cabno", getItemDtl.get(i).get("cabinetNumber").toString());
                tempDoc.put("azdat", getItemDtl.get(i).get("actualLoadingDate").toString().replaceAll("-", ""));
                tempDoc.put("algort", getItemDtl.get(i).get("whsLocationCode").toString());
                tempDoc.put("algobe", getItemDtl.get(i).get("whsLocationDesc").toString());
                tempDoc.put("sealn", getItemDtl.get(i).get("leadSeal") == null ? "" : getItemDtl.get(i).get("leadSeal"));
                tempDoc.put("matnr", getItemDtl.get(i).get("materialNo").toString());
                tempDoc.put("maktx", getItemDtl.get(i).get("materialZhDesc").toString());
                /** vrkme 销售单位；aemng 实际装箱件数；meins 基本计量单位——留空; ameng 实际装箱数量——留空; cdQty 报关件数; */
                tempDoc.put("vrkme", getItemDtl.get(i).get("unitSales").toString());
                tempDoc.put("meins", getItemDtl.get(i).get("basicUnit").toString());
                tempDoc.put("ameng", getItemDtl.get(i).get("actualOperateQty") == null ? "" : getItemDtl.get(i).get("actualOperateQty").toString());
                /** bzgg 包装规格; jyjg 货柜检验结果; kgBrgew 空柜重量（KG）; wxBrand 外箱品牌;  */
                tempDoc.put("bzgg", getItemDtl.get(i).get("packingSpec") == null ? "" : getItemDtl.get(i).get("packingSpec"));
                tempDoc.put("jyjg", getItemDtl.get(i).get("cabinetCheckResult") == null ? "" : getItemDtl.get(i).get("cabinetCheckResult"));
                tempDoc.put("kgBrgew", getItemDtl.get(i).get("emptyCabinetWeight") == null ? "" : getItemDtl.get(i).get("emptyCabinetWeight").toString());
                tempDoc.put("wxBrand", getItemDtl.get(i).get("containerBrand") == null ? "" : getItemDtl.get(i).get("containerBrand"));
                if ("M".equals(goodsType)) {
                    /** aemng 实际装箱件数；cdQty 报关件数； */
                    tempDoc.put("cdQty", null == getItemDtl.get(i).get("customsDeclarationNumber") ? "" : getItemDtl.get(i).get("customsDeclarationNumber").toString());
                    tempDoc.put("aemng", null == getItemDtl.get(i).get("actualPackingNumber") ? "0" : getItemDtl.get(i).get("actualPackingNumber").toString());
                    tempDoc.put("colorCode", getItemDtl.get(i).get("colorNumber") == null ? "" : getItemDtl.get(i).get("colorNumber").toString());
                    tempDoc.put("zh", getItemDtl.get(i).get("thickness") == null ? "" : getItemDtl.get(i).get("thickness").toString());
                    tempDoc.put("cpsjpjBrgew", getItemDtl.get(i).get("actualAvgGrossWeight") == null ? "" : getItemDtl.get(i).get("actualAvgGrossWeight").toString());
                } else {
                    /** 备件货实际装箱件数，由国内实际数量字段、销售单位和基本单位换算分子分母关系，计算得出保留三位小数；cdQty 报关件数备件货传空； */
                    double actualOperateQty = getItemDtl.get(i).get("actualOperateQty") == null ? 0 : Double.parseDouble(getItemDtl.get(i).get("actualOperateQty").toString());
                    double numerator = Double.parseDouble(getItemDtl.get(i).get("numerator").toString());
                    double denominator = Double.parseDouble(getItemDtl.get(i).get("denominator").toString());
                    String actualPackingNumber = String.format("%.3f", (denominator / numerator) * actualOperateQty);
                    tempDoc.put("aemng", actualPackingNumber);
                    tempDoc.put("cdQty", "");
                    tempDoc.put("colorCode", "");
                    tempDoc.put("zh", "");
                    tempDoc.put("cpsjpjBrgew", "");
                    /** 备件货国内出库结果回传SAP信息结构带上箱码、箱码行号 —— 20211116 */
                    tempDoc.put("serial", getItemDtl.get(i).get("boxNoteItem").toString());
                    tempDoc.put("boxCode", getItemDtl.get(i).get("boxNote").toString());
                }
                tempDoc.put("jzry", getItemDtl.get(i).get("monitorLoading") == null ? "" : getItemDtl.get(i).get("monitorLoading"));
                tempDoc.put("zgfs", getItemDtl.get(i).get("loadingType") == null ? "" : getItemDtl.get(i).get("loadingType"));
                tempDoc.put("mark", getItemDtl.get(i).get("remarks") == null ? "" : getItemDtl.get(i).get("remarks"));
                tempDoc.put("pkmakYn", "N");
                tempDoc.put("del", "");
                tempDoc.put("importFlag", "A");

                PT_INPUT_LIST.add(tempDoc);
            }
            MapItems.put("PT_INPUT_LIST", PT_INPUT_LIST);
            if ("1".equals(confirmLastSubmission)) {
                JSONObject tempDoc = new JSONObject();
                tempDoc.put("xdnum", packageList);
                tempDoc.put("xzhj", "2");
                tempDoc.put("curtu", "X");
                PT_STATE.add(tempDoc);
                MapItems.put("PT_STATE", PT_STATE);
                PT_CONFIRM = this.baseMapper.getPackageListBoxNote(packageList);
                MapItems.put("PT_CONFIRM", PT_CONFIRM);
            }
            REQUEST_DATA.put("MapItems", MapItems);
            REQUEST.put("ESB_ATTRS", ESB_ATTRS);
            REQUEST.put("REQUEST_DATA", REQUEST_DATA);

            obj.put("REQUEST", REQUEST);
            return ResponseResult.success().add("obj", obj);
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    /**
     * 备件货出库任务详情保存:
     * 1.检验出库任务表头当前状态、目标状态状态是否可更新；
     * 2.删除下架任务详情，重新建下架任务详情；表头不更新；记录下架任务操作日志；
     * 3.出库任务详情——SAP_PACKAGE_BOX 删除计划外行数，更新计划内行数据；
     * 4.更新出库任务表头状态及其他字段，记录表头更新操作日志；
     *
     * @param doc
     * @param userId
     * @return
     */
    @Override
    @Transactional
    public ResponseResult saveWhsOutX(JSONObject doc, String userId) {
        try {
            //String whsOutUuid = doc.getJSONObject("whsOut").getString("uuid");
            ResponseResult checkUpdateWhsOutXValidate = checkUpdateWhsOutX(doc);
            if (checkUpdateWhsOutXValidate.getCode() != 200) {
                return checkUpdateWhsOutXValidate;
            }
            int resp = updateShelfOnTaskAndDtl(doc, userId);
            if (resp < 0) {
                throw new Exception("下架任务更新过程执行异常 updateShelfOnTaskAndDtl");
            }
            int result = updateWhsOutAndDtl(doc, userId);
            if (result < 0) {
                throw new Exception("出库任务更新过程执行异常 updateWhsOutAndDtl ");
            }
            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    ResponseResult checkUpdateWhsOutX(JSONObject doc) {
        try {
            JSONObject whsOutObject = doc.getJSONObject("whsOut");
            String whsOutUuid = whsOutObject.getString("uuid");
            String currentStatus = whsOutObject.getString("currentStatus");
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            if (whsOut == null) {
                return ResponseResult.error("出库任务不存在");
            }
            if (!currentStatus.equals(whsOut.getTaskStatus())) {
                return ResponseResult.error("出库任务状态已更新请刷新后重试");
            }
            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    int updateShelfOnTaskAndDtl(JSONObject doc, String userId) {
        try {
            JSONObject shelfOnTaskObject = doc.getJSONObject("shelfOnTask");
            JSONArray shelfOnTaskDtlArray = doc.getJSONArray("shelfOnTaskDtls");
            String shelfOnTaskUuid = shelfOnTaskObject.getString("uuid");
            if (shelfOnTaskUuid != null) {
                iShelfOnTaskService.deleteShelfOnTaskDtl(shelfOnTaskUuid);
            }
            if (shelfOnTaskDtlArray.size() > 0) {
                for (int i = 0; i < shelfOnTaskDtlArray.size(); i++) {
                    JSONObject dtlObject = shelfOnTaskDtlArray.getJSONObject(i);
                    ShelfOnTaskDtl shelfOnTaskDtl = new ShelfOnTaskDtl();
                    double actualOperateQty = StringUtils.isEmpty(dtlObject.getString("actualOperateQty")) ?
                            0 : dtlObject.getDouble("actualOperateQty");
                    shelfOnTaskDtl.setUuid(shelfOnTaskUuid);
                    shelfOnTaskDtl.setTaskRowId(dtlObject.getString("taskRowId"));
                    shelfOnTaskDtl.setBoxNote(dtlObject.getString("boxNote"));
                    shelfOnTaskDtl.setQuantity(dtlObject.getDouble("quantity"));
                    shelfOnTaskDtl.setTargetStorage(dtlObject.getString("targetStorage"));
                    shelfOnTaskDtl.setActualOperateQty(actualOperateQty);
                    shelfOnTaskDtl.setUnplanned(dtlObject.getString("unplanned"));
                    /** 新建下架任务详情 */
                    iShelfOnTaskService.insertShelfOnTaskDtl(shelfOnTaskDtl);
                }
            }
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(shelfOnTaskUuid, userId, UPDATE_DEDUCT_SHELF_TASK);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    int updateWhsOutAndDtl(JSONObject doc, String userId) {
        try {
            JSONObject whsOutObject = doc.getJSONObject("whsOut");
            JSONArray whsOutDtlArray = doc.getJSONArray("whsOutDtl");
            String whsOutUuid = whsOutObject.getString("uuid");
            /** 按出库任务uuid，删除计划外的行项目 */
            iSapPackageBoxService.deleteUnplannedBN(whsOutUuid, "1");
            if (whsOutDtlArray.size() > 0) {
                for (int i = 0; i < whsOutDtlArray.size(); i++) {
                    JSONObject whsOutDtlObject = whsOutDtlArray.getJSONObject(i);
                    String unplanned = whsOutDtlObject.getString("unplanned");
                    String packageList = whsOutDtlObject.getString("packageList");
                    double actualOperateQty = StringUtils.isEmpty(whsOutDtlObject.getString("actualOperateQty")) ?
                            0 : whsOutDtlObject.getDouble("actualOperateQty");
                    String item = whsOutDtlObject.getString("item");
                    String boxNote = whsOutDtlObject.getString("boxNote");
                    String containerBrand = whsOutDtlObject.getString("containerBrand");
                    String packingSpec = whsOutDtlObject.getString("packingSpec");
                    String loadingType = whsOutDtlObject.getString("loadingType");
                    if ("1".equals(unplanned)) {
                        /** 计划外的行项目，按箱码新增到 装箱单箱码关系表中 */
                        iSapPackageBoxService.addSapPackageBox(packageList, boxNote, actualOperateQty, unplanned, whsOutUuid, containerBrand, packingSpec, loadingType);
                    } else {
                        /** 计划之内的行项目，按装箱单、箱码 更新出库明细行项目 */
                        iSapPackageBoxService.updateSapPackageBox(packageList, boxNote, actualOperateQty, whsOutUuid, containerBrand, packingSpec, loadingType);
                    }
                }
            }
            WhsOut whsOut = new WhsOut();
            whsOut.setUuid(whsOutUuid);
            whsOut.setTaskStatus(whsOutObject.getString("targetStatus"));
            whsOut.setCabinetNumber(whsOutObject.getString("cabinetNumber"));
            whsOut.setActualLoadingDate(whsOutObject.getString("actualLoadingDate"));
            whsOut.setMonitorLoading(whsOutObject.getString("monitorLoading"));
            whsOut.setLoadingType(whsOutObject.getString("loadingType"));
            whsOut.setRemarks(whsOutObject.getString("remarks"));
            whsOut.setCabinetCheckResult(whsOutObject.getString("cabinetCheckResult"));
            whsOut.setLeadSeal(whsOutObject.getString("leadSeal"));
            if(StringUtils.isNotEmpty(whsOutObject.getString("operateUser"))){
                whsOut.setOperateUser(whsOutObject.getString("operateUser"));
            }
            /** 更新出库任务表头 */
            this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_TASK_UPDATE);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    int updateSapPackageList(String userId, String packageList, String targetStatus) {

        this.baseMapper.updateSapPackageList(packageList, targetStatus);
        if ("1".equals(targetStatus)) {
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(packageList, userId, UPDATE_SAP_PACKAGE_Withdraw);
        } else if ("2".equals(targetStatus)) {
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(packageList, userId, CLOSE_SAP_PACKAGE_LIST);
        }

        return 1;
    }

    @Override
    public ResponseResult getWhsOutDtlList(String packageList, String cabinetSerialNumber, String werks, String whsLocationCode) {
        try {
            if (StringUtils.isEmpty(packageList) || StringUtils.isEmpty(werks) || StringUtils.isEmpty(whsLocationCode)) {
                return ResponseResult.error("请求参数错误请重试");
            }
            if (StringUtils.isEmpty(cabinetSerialNumber)) {
                cabinetSerialNumber = null;
            }
            return ResponseResult.success().add("whsOutDtlList", this.baseMapper.getWhsOutDtlList(packageList, cabinetSerialNumber, werks, whsLocationCode));
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    @Override
    public ShelfOnTask getShelfOnTaskByRelateDocNumber(String whsOutUuid, String taskType) {
        // TASK_TYPE = 1 上架任务; TASK_TYPE = 2 下架任务;
        return iShelfOnTaskService.getByRelateDocNumber(whsOutUuid, taskType);
    }

    @Override
    public List<ShelfOnTaskDtl> getShelfOnTaskDtlByRelateDocNumber(String shelfOnTaskUuid) {
        return iShelfOnTaskService.getDtlByUuid(shelfOnTaskUuid);
    }

    /** 装箱单出库结果删除接口 */
    @Override
    public ResponseResult whsOutCancel(String packageList, String whsOutUuid, String userId){
        try {
            JSONObject dataToSap = new JSONObject();
            JSONObject REQUEST = new JSONObject();
            JSONObject ESB_ATTRS = new JSONObject();
            JSONObject REQUEST_DATA = new JSONObject();
            JSONObject Head = new JSONObject();

            ESB_ATTRS.put("App_ID", "SPMS");
            ESB_ATTRS.put("Application_ID", "00020000000002");
            ESB_ATTRS.put("Transaction_ID", UUID.randomUUID().toString());

            Head.put("imXdnum", packageList);
            Head.put("imZspmsTaskid", whsOutUuid);
            Head.put("imImportFlag", "A");

            REQUEST_DATA.put("Head", Head);
            REQUEST_DATA.put("Operation", "Z_ZXD_DEL");
            REQUEST_DATA.put("Type", "ZXD_DEL");

            REQUEST.put("ESB_ATTRS", ESB_ATTRS);
            REQUEST.put("REQUEST_DATA", REQUEST_DATA);
            dataToSap.put("REQUEST", REQUEST);
            JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon((dataToSap)));
            System.out.println("\n装箱单出库结果删除接口\ndataToSap ====== " + dataToSap + "\nmessage ====== " + message);
            if (message.containsKey("RESPONSE") && message.getJSONObject("RESPONSE").containsKey("RETURN_DATA")
                    && "S".equals(message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA").getString("EX_TYPE"))){
                iSapDeliveryNoteLogService.insertSapRequestRecord(whsOutUuid, userId, WHS_OUT_CANCEL, message.toJSONString(), dataToSap.toJSONString());
                return ResponseResult.success().add("sendSapReturnedMessage", message).add("dataToSap", dataToSap);
            }
            iSapDeliveryNoteLogService.insertSapRequestRecord(whsOutUuid, userId, WHS_OUT_CANCEL_ERROR, message.toJSONString(), dataToSap.toJSONString());
            return ResponseResult.error().add("sendSapReturnedMessage", message).add("dataToSap", dataToSap);
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    /**
     出库任务状态:0待执行，1执行中，2出库完成，3任务关闭，4提交SAP成功
     出库任务关闭操作前，执行出库任务数据是否能被操作校验：
       1.判断该出库任务状态是否能被关闭；出库任务状态为 0，1 的才能被关闭；
       2.判断该出库任务是否有关联的 未完成的上下架任务，如果有则返回错误信息；
       3.更新前判断是否是同装箱单下最后一个未完成的出库任务：
       3.1.如果不是则直接将出库任务状态更新为任务关闭；记录日志信息；
       3.2.如果是则将 "此装箱单是最后一次提交SAP" 的信息告知SAP；连同该装箱单下所有备件箱码及明细告知SAP；请求SAP成功
     则更新该出库任务状态关闭，记录该出库任务提交SAP日志和任务关闭日志，更新装箱单状态完成，记录装箱单更新日志；
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult whsOutClose(String whsOutUuid, String userId, String remarks) {
        ResponseResult checkResult = checkDataValidate(whsOutUuid);
        if (checkResult.getCode() != 200){
            return checkResult;
        }
        String confirmLastSubmission = "0";
        /** 获取出库任务同一个装箱单下所有  未成功提交SAP  的出库任务数量，来判断当前任务是否是最后一个提交。最后一个提交会多出 PT_STATE 一些字段； */
        /** 20210713 修改最后一次提交判断逻辑：同一个装箱单下的出库任务，状态不为 3（任务关闭），不为4（提交SAP成功）的出库任务数量如果为 1，则表示最后一次提交
         * 如果关闭的任务是同装箱单下最后一个关闭的出库任务，则需要提交相关信息给到SAP告知是最后一次提交；记录日志；
         * 如果关闭任务不是同装箱单下最后一个关闭的出库任务，则仅在SPMS关闭任务，记录日志即可；
         * */
        String getWhsOutStatusCount = this.baseMapper.getWhsOutStatusCount(whsOutUuid);
        if ("1".equals(getWhsOutStatusCount)) {
            confirmLastSubmission = "1";
        }
        if (!"1".equals(confirmLastSubmission)){
            WhsOut whsOut = new WhsOut();
            whsOut.setTaskStatus("3");
            whsOut.setRemarks(remarks);
            try {
                this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
                iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_CLOSE);
                return ResponseResult.success().add("msg", "不是同装箱单下最后一次提交SAP的出库任务");
            }catch (Exception e){
                e.printStackTrace();
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ResponseResult.error().add("error", e.getMessage());
            }
        }
        WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        JSONObject closeDataToSap = whsOutCloseToSap(whsOut.getPackageList());
        try {
            JSONObject sapReturnedMessage = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(closeDataToSap));
            if (sapReturnedMessage.containsKey("RESPONSE") && sapReturnedMessage.getJSONObject("RESPONSE").containsKey("RETURN_DATA")
                    && "S".equals(sapReturnedMessage.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA").getString("EX_TYPE"))){
                WhsOut out = new WhsOut();
                out.setTaskStatus("3");
                out.setRemarks(remarks);
                this.baseMapper.update(out, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
                iSapDeliveryNoteLogService.insertSapRequestRecord(whsOutUuid, userId, WHS_OUT_CLOSE,
                        sapReturnedMessage.toJSONString(), closeDataToSap.toJSONString());
                /** 同装箱单下最后一个关闭的出库任务，需要更新装箱单任务状态为 2-关闭，记录关闭日志； */
                updatePLTaskStatus(whsOut.getPackageList(), "2", userId);
                return ResponseResult.success().add("msg", "是同装箱单下最后一次提交SAP的出库任务")
                        .add("dataToSap", closeDataToSap).add("sapReturnedMessage", sapReturnedMessage);
            } else {
                iSapDeliveryNoteLogService.insertSapRequestRecord(whsOutUuid, userId, WHS_OUT_CLOSE_ERROR,
                        sapReturnedMessage.toJSONString(), closeDataToSap.toJSONString());
                return ResponseResult.error().add("msg", "是同装箱单下最后一次提交SAP的出库任务")
                        .add("dataToSap", closeDataToSap).add("sapReturnedMessage", sapReturnedMessage);
            }
        }catch (FeignException exception){
            exception.printStackTrace();
            return ResponseResult.error().add("error", exception.getMessage());
        }catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }


    ResponseResult checkDataValidate(String whsOutUuid){
        if (StringUtils.isEmpty(whsOutUuid)){
            return ResponseResult.error("请求参数错误");
        }
        WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        if (null == whsOut){
            return ResponseResult.error("获取出库任务失败");
        }
        String taskStatus = whsOut.getTaskStatus();
        if (!"0".equals(taskStatus) && !"1".equals(taskStatus)){
            return ResponseResult.error("任务当前状态不可关闭");
        }
        List<ShelfOnTask> shelfOnTaskList = iShelfOnTaskService.getUnClosedTasks(whsOutUuid);
        if (shelfOnTaskList != null && shelfOnTaskList.size() > 0){
            return ResponseResult.error("此出库任务存在未完成的下架或上架任务，请先完成关联的上下架任务");
        }
        return ResponseResult.success();
    }

    JSONObject whsOutCloseToSap(String packageList){
        JSONObject obj = new JSONObject();
        JSONObject REQUEST = new JSONObject();
        JSONObject ESB_ATTRS = new JSONObject();
        JSONObject REQUEST_DATA = new JSONObject();
        JSONObject Head = new JSONObject();
        JSONObject MapItems = new JSONObject();
        JSONArray PT_INPUT_LIST = new JSONArray();
        JSONArray PT_STATE = new JSONArray();
        List<Map<String, Object>> PT_CONFIRM = new ArrayList<>();

        ESB_ATTRS.put("App_ID", "SPMS");
        ESB_ATTRS.put("Application_ID", "00020000000002");
        ESB_ATTRS.put("Transaction_ID", UUID.randomUUID().toString());

        Head.put("imImportFlag", "A");

        REQUEST_DATA.put("Head", Head);
        REQUEST_DATA.put("Operation", "Z_ZXD_IMPROT_AND_CONFIRM");
        REQUEST_DATA.put("Type", "IMPROT_AND_CONFIRM");

        MapItems.put("PT_INPUT_LIST", PT_INPUT_LIST);
        REQUEST_DATA.put("MapItems", MapItems);
        JSONObject tempDoc = new JSONObject();
        tempDoc.put("xdnum", packageList);
        tempDoc.put("xzhj", "2");
        tempDoc.put("curtu", "X");
        PT_STATE.add(tempDoc);
        MapItems.put("PT_STATE", PT_STATE);
        PT_CONFIRM = this.baseMapper.getPackageListBoxNote(packageList);
        MapItems.put("PT_CONFIRM", PT_CONFIRM);

        REQUEST.put("ESB_ATTRS", ESB_ATTRS);
        REQUEST.put("REQUEST_DATA", REQUEST_DATA);

        obj.put("REQUEST", REQUEST);
        return obj;
    }

    void updatePLTaskStatus(String packageList, String packageListTaskStatus, String userId){
        this.baseMapper.updatePLTaskStatus(packageList, packageListTaskStatus);
        iSapDeliveryNoteLogService.insertSapRequestRecord(packageList, userId, CLOSE_SAP_PACKAGE_LIST, "", "");
    }
}
